<template>
  <!-- elementUI布局 -->
  <el-container class="home_container">
    <!-- 头部区域 -->
    <el-header>
      <div class="logo_wrapper">
        <img
          src="../assets/logo.png"
          alt="logo"
        />
        <span>电商后台管理系统</span>
      </div>
      <!-- 个人中心（可修改个人信心和退出登录） -->
      <!-- el-dropdown中要用@click必须使用split-button将拉下菜单转换为按钮才行，不转就只能用@command，然后通过每个子菜单的command值进行判断再进行分别调用 -->
      <el-dropdown @command="editDialog">
        <span class="el-dropdown-link">
          欢迎回来：{{editForm.username}}<i class="el-icon-arrow-down el-icon--right"></i>
        </span>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item
            icon="el-icon-plus"
            command="edit"
          >个人信息</el-dropdown-item>
          <el-dropdown-item
            icon="el-icon-circle-plus"
            command="logout"
          >退出登录</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </el-header>
    <!-- 页面主体区域 -->
    <el-container>
      <!-- 左侧导航栏 -->
      <!-- 让侧边导航栏的宽度随着是否折叠来进行修改，所以不能用width，而是要用:width进行数据绑定
      当isCollapse为true时，表示折叠菜单，所以宽度设置为64px，当其为fasle时，表示不折叠，则设置宽度为200px
      -->
      <el-aside :width="isCollapse ? '64px' : '200px'">
        <!-- 导航栏的折叠按钮 -->
        <div
          class="toggle-button"
          @click="toggleCollapse"
        >
          <el-tooltip
            effect="dark"
            content="折叠|展开"
            placement="top"
            :enterable="false"
          >
            <i class="el-icon-s-operation"></i>
          </el-tooltip>
        </div>
        <!-- active-text-color ==> 当菜单被点击激活时的颜色 -->
        <!-- unique-opened 默认为false，允许多个菜单同时展开，设置为true表示只能同时打开一个菜单
        因为是element的属性，要用 : 来绑定，否则直接="true"那就是个字符串，而不是布尔值
        或者，直接写上 unique-opened 就行，因为不写表示默认false，写上就是true，-->
        <!-- :collapse="false" 默认就是false，表示不折叠菜单 所以动态绑定一个data中的判别布尔值进行切换-->
        <!-- :collapse-transition="false" 设置折叠菜单时没有动画，默认为true -->

        <!-- :router="true" 表示开启路由模式，默认为false，开启后，会在激活导航时以 index 作为 path 进行路由跳转
            例如：:index="/test"，点击这个菜单时，路由占位符中就会加载这个/test对应的组件
            http://localhost:8080/#/test  路径就会变成这样
            如果没有在route中的index.js文件中的/home下的children中写入 path: '/test',
            那么默认还是在主体的App.vue中加载，而不是在Home.vue组件中的路由占位符中加载，
            如果在/home中的子路径下写了path: '/test'，那么就会在Home.vue组件中的路由占位符中加载test组件！！
        -->

        <!-- default-active="path" 表示当前激活菜单的 index，用于让点击的菜单高亮 -->
        <el-menu
          :unique-opened="true"
          background-color="#333744"
          text-color="#fff"
          :collapse="isCollapse"
          :collapse-transition="false"
          :router="true"
          active-text-color="#409bff"
          :default-active="activePath"
        >
          <!-- 一级菜单 -->
          <!-- Eslint下，有v-for那就必须要有:key!! -->
          <!-- index="1" 如果所有的菜单的index都一样，那点击一个，所有的都有反应，所以每个要不一样 -->
          <!-- index="1" index只是识别字符串，不识别数字，所以在id后加上 +'' 转成字符串 -->
          <el-submenu
            :index="item.id.toString()"
            v-for="item in menulist"
            :key="item.id"
          >
            <!-- 菜单的模板区域 -->
            <template slot="title">
              <!-- 一级菜单的图标 -->
              <!-- 因为要循环的时候动态绑定图标，所以class="el-icon-user"要写成 :class="iconsObj[item.id]" -->
              <i :class="iconsObj[item.id]"></i>
              <!-- 一级菜单的文字 -->
              <span>{{item.authName}}</span>
            </template>

            <!-- 二级菜单 -->
            <!--当index是从后端传入的时候，要写成:index，通过绑定进行数据获取
            而且必须是字符串形式，所以可以用这种 +'' 也可以直接.toString() 转换成字符串形式-->
            <!-- :index="'/'+subItem.path"  但是现在好像会在路由时自动添加 / -->
            <!--在菜单中的 :index="users" 相当于 this.$router.push('users')，会根据这个path路径进行跳转 -->
            <!-- saveNavState(subItem.path) 当点击二级菜单的时候，保存这个菜单的路径(通过传参将path传给自己写的函数),
            因为要在每次刷新页面后任然能定位在上次点击的页面-->
            <el-menu-item
              :index="subItem.path"
              v-for="subItem in item.children"
              :key="subItem.id"
              @click="saveNavState(subItem.path)"
            >
              <!-- 菜单的模板区域 -->
              <template slot="title">
                <!-- 二级菜单的图标 -->
                <i class="el-icon-menu"></i>
                <!-- 二级菜单的文字 -->
                <span>{{subItem.authName}}</span>
              </template>
            </el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>

      <!-- 右侧内容主体区域 -->
      <el-main>
        <!-- 路由占位符 -->
        <!-- 当点击菜单时，根据菜单的路径，加载组件到这个路由占位符里，而不是跳转整个页面 -->
        <router-view></router-view>
      </el-main>

    </el-container>

    <!-- 修改用户按钮的对话框Dialog -->
    <el-dialog
      title="修改用户"
      :visible.sync="editDialogVisible"
      width="50%"
      @close="editDialogClosed"
    >
      <!-- 对话框内容主体区域 -->
      <!-- :rules="addFormRules" 因为用的邮箱和手机号验证规则是和add的一样的，所以直接用addFormRules进行格式验证 -->
      <el-form
        :model="editForm"
        :rules="addFormRules"
        ref="editFormRef"
        label-width="70px"
        status-icon
      >
        <el-form-item label="用户名">
          <!-- 双向绑定input输入框的数据到本地data中的addForm中的username,这样本地data中的username是啥，这里面就显示啥 -->
          <!-- disabled 禁止修改 -->
          <el-input
            v-model="editForm.username"
            disabled
          ></el-input>
        </el-form-item>

        <el-form-item
          label="邮箱"
          prop="email"
        >
          <el-input v-model="editForm.email"></el-input>
        </el-form-item>

        <el-form-item
          label="手机号"
          prop="mobile"
        >
          <el-input v-model="editForm.mobile"></el-input>
        </el-form-item>

      </el-form>
      <!-- 底部按钮区域 -->
      <span
        slot="footer"
        class="dialog-footer"
      >
        <!--  @click="aeditDialogVisible = false" 表示只要点击了取消就隐藏对话框 -->
        <el-button @click="editDialogVisible = false">取 消</el-button>
        <el-button
          type="primary"
          @click="editUserConfirm"
        >确 定</el-button>
      </span>
    </el-dialog>
  </el-container>

</template>

<script>
export default {
  data () {
    return {
      // 将在login时保存的当前用户信息保存在本地data中，方便加载个人中心用
      currentUserObj: {
        // ! 为本地的请求头对象(headerObj)添加currentUserId字段，并将sessionStorage中的currentUserId值赋值过去
        currentUserId: window.sessionStorage.getItem('currentUserId')
      },
      // 左侧菜单数据存储
      menulist: [],
      /**
         * !因为一级菜单是通过v-for进行循环生成的，所以要通过 图标对象来动态的添加
         * !所以在data里放一个iconObj来存放每个对应一级菜单id的对应图标，循环的时候根据id来添加class，
         * 这样循环添加的每个菜单的图标就不一样了
         * 注意数字是对应菜单的id，不能错，否则就没有图标
         */
      iconsObj: {
        // 因为在main.js中引入了阿里图标库，所以可以用阿里的图标
        125: 'iconfont icon-users',
        103: 'iconfont icon-tijikongjian',
        101: 'iconfont icon-shangpin',
        102: 'iconfont icon-danju',
        145: 'iconfont icon-baobiao'
      },
      // 是否展开/关闭侧边栏
      isCollapse: false,
      // 被激活的二级菜单path
      activePath: '',
      // 判断修改用户的对话框是否显示的布尔值
      editDialogVisible: false,
      // 编辑用户信息时，根据Id查询到的用户信息本地data中存储
      editForm: {
        username: '',
        email: '',
        mobile: ''
      },
      // 添加表单的验证规则
      addFormRules: {
        // trigger:'blur' 失去焦点时触发验证时机
        username: [{ required: true, message: '请输入用户名', trigger: 'blur' }, { min: 3, max: 10, message: '用户名长度在3-10位之间', trigger: 'blur' }],
        password: [{ required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 12, message: '用户名长度在6-12位之间', trigger: 'blur' }],
        email: { required: true, message: '请输入邮箱', trigger: 'blur' },
        mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' }, { min: 11, max: 11, message: '手机号长度不正确', trigger: 'blur' }]
      }
    }
  },
  // 声明周期函数，一打开页面立即运行
  created () {
    // 获取当前登录用户的信息，用于加载欢迎回来：用户名
    this.getcurrentUser()
    // 调用这个函数，获取后端菜单列表
    this.getMenuList()
    // 用于当刷新时，获取到存储在sessionStorage的激活状态的二级菜单地址activePath
    this.activePath = window.sessionStorage.getItem('activePath')
  },

  methods: {
    // 获取当前登录用户的信息，用于加载欢迎回来：用户名
    async getcurrentUser () {
      // console.log(this.currentUserObj)
      // 通过接口，由用户Id查询到这个用户的数据
      const { data: res } = await this.$axios.get('users/' + this.currentUserObj.currentUserId)
      if (res.meta.status !== 200) {
        return this.$message.error('查询用户信息失败')
      } else {
        // console.log(res)
        // 把通过这行Id查询到的信息保存给本地data中的editForm
        this.editForm = res.data
      }
    },
    // 个人中心的个人信息点击事件，通过windows保存的userId查询到的对应的数据，用于展示编辑对话框
    editDialog (command) {
      if (command === 'edit') {
        // 展示edit编辑对话框
        this.editDialogVisible = true
      } else {
        // 退出登录
        this.logout()
      }
    },
    // 编辑用户对话框中的确定按钮事件,点击确定后先进行所有表单的规则验证，防止漏填或填错
    editUserConfirm () {
      // 通过找到form表单的ref引用名，调用validate((valid)=>{})函数，进行表单的与验证，数据合格，valid会返回true，反之，返回false
      this.$refs.editFormRef.validate(async (valid) => {
        // 预验证不通过
        if (!valid) { return false }
        // 预验证通过，可以发起axios请求
        const { data: res } = await this.$axios.put('users/' + this.editForm.id, { email: this.editForm.email, mobile: this.editForm.mobile })
        // 根据接口返回的状态码进行操作是否成功的判断
        if (res.meta.status !== 200) {
          this.$message.error('修改用户失败')
        } else {
          // 数据提交后，对话框的作用也结束了，所以关闭对话框,将addDialogVisible置为false
          this.editDialogVisible = false
          // msg提示修改成功
          this.$message.success('修改用户信息成功')
          // 因为进行了修改，所以刷新用户列表，也就是重新调用获取用户的函数
          this.getcurrentUser()
        }
      })
    },
    // 监听编辑用户对话框的关闭事件,每次关闭对话框都进行表单的重置
    editDialogClosed () {
      // 通过找到form表单的ref引用名，调用resetFields()函数，进行表单的重置操作
      this.$refs.editFormRef.resetFields()
    },
    // 退出登录
    logout () {
      this.$message.success('退出成功,欢迎再次登录！')
      // !因为我们是基于token来判断是否有权限访问其他页面（是否登录），所以退出登录就是 清除token值
      // !清空sessionStorage中保存的所有东西
      // 也就清除我们登录的时候保存的token值，这样相当于没登录了
      // 也清除了我们点击导航菜单时保存的激活状态路径
      window.sessionStorage.clear()
      // 然后跳转回登录页面
      this.$router.push('/login')
    },
    // 获取后端菜单列表函数
    async getMenuList () {
      // 通过menus接口获取菜单数据
      const { data: res } = await this.$axios.get('menus')
      // 控制台打印出获取到的菜单数据
      // console.log(res)
      // 如果返回的状态码不是200则表示获取失败
      if (res.meta.status !== 200) {
        // 通过elementUI的msg弹窗展示返回的msg信息
        return this.$message.error(res.meta.msg)
      }
      this.menulist = res.data
    },
    // 折叠侧边栏菜单的函数
    toggleCollapse () {
      // ! 因为isCollapse在data中是个布尔值，这样写就是取反的意思，如果为true则变成false
      this.isCollapse = !this.isCollapse
    },
    // 保存点击的二级菜单的path地址到sessionStorage中，以次保存菜单的激活状态
    saveNavState (saveActivePath) {
      window.sessionStorage.setItem('activePath', saveActivePath)
      // 当再次点击其他菜单时，重新给data中的activePath赋值
      this.activePath = saveActivePath
    }
  }
}
</script>

<style lang="less" scoped>

  //! elementUI的标签名就是它的类名,当然也可以自己再给它加一个class类名添加样式
  .home_container {
    height: 100%;
    width: 100%;
  }

  .el-header {
    // el-header默认的样式里有左侧的内边距，我们可以自己覆盖掉，这样就不会有内边距了
    // padding-left: 0;
    background-color: #B3C0D1;
    color: #333;
    text-align: center;
    font-size: 20px;
    display: flex;
    justify-content: space-between;

    .logo_wrapper {
      width: auto;
      height: 100%;
      display: flex;
      align-items: center;

      span {
        margin-left: 15px;
      }

      img {
        width: 100px;
        height: 100%;
        border-radius: 50%;
        background: #B3C0D1;
      }
    }
    // 欢迎回来中的样式
    .el-dropdown{
      display: flex;
      align-items: center;
    }
    .el-dropdown-link {
      font-size: 20px;
      // 鼠标移入时展示位手指
      cursor: pointer;
      color: #ffffff;
    }
  }

  .el-aside {
    background-color: #333744;
    color: #333;

    // 因为默认的.el-menu样式中有个border-right: solid 1px #e6e6e6; 会导致菜单打开时凹凸不平，所以我们自己取消这个边框
    .el-menu {
      border-right: none;
    }
  }

  // 设置侧边栏中的iconfont图标和文字之间的距离
  .el-aside .iconfont {
    margin-right: 10px;
  }

  .toggle-button {
    background: #4a5064;
    color: #fff;
    text-align: center;
    font-size: 10ps;
    line-height: 24px;
    // 文字之间的距离
    letter-spacing: 0.2em;
    // 当鼠标移入时变成手指
    cursor: pointer;
  }

  .el-main {
    background-color: #E9EEF3;
    color: #333;
  }

</style>
